#!/usr/bin/python
#
# wowstatus.py
# Copyright (C) Matthew Howle 2008 <mdhowle@gmail.com>
# 
# main.py is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# main.py is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

import pygtk
import sys
pygtk.require('2.0')
import urllib
import gtk
import gobject
import gnomeapplet
import gnome.ui
import gtk.glade
import gtk.gdk
import os.path
import string
import gconf
import time

class Pref(object):
	"Preference handling (GUI and the loading and saving of settings)"

	def __init__(self, applet=None, show=True):
		# Applet = the instance of WSApplet to apply settings
		# Show = Boolean value whether to show the GUI preference window

		self.loadCfg()
		if applet is not None:
			self.app = applet

		if show:
			window = gtk.Window(gtk.WINDOW_TOPLEVEL)
			window.set_title("Preferences")
			window.connect("destroy", window.destroy)


			vbox = gtk.VBox(False, 5)
			vbox.set_border_width(5)
			window.add(vbox)
			vbox.show()

			frame = gtk.Frame("Servers")
			frame.set_border_width(5)
			frame.set_shadow_type(gtk.SHADOW_OUT)
			vbox.add(frame)
			frame.show()

			scrolled_window = gtk.ScrolledWindow()
			scrolled_window.set_usize(450, 250)
			frame.add(scrolled_window)
			scrolled_window.show()

			self.list = gtk.ListStore(str)
			self.tview = gtk.TreeView(model=self.list)
			self.tview.set_headers_visible(False)
			self.col = gtk.TreeViewColumn('Servers')
			self.cell = gtk.CellRendererText()
	
			self.tview.append_column(self.col)
			self.col.pack_start(self.cell)
			self.col.add_attribute(self.cell, 'text', 0)

			scrolled_window.add_with_viewport(self.tview)
			self.tview.show()

			hbox = gtk.HBox(False, 5)
			hbox.set_border_width(5)
			vbox.add(hbox)
			hbox.show()

			label = gtk.Label("Update Interval (min)")
			label.show()
			hbox.add(label)
		
			adj = gtk.Adjustment(self.Interval, 1.0, 9999.0, 1.0, 10.0, 0.0)
			self.sbutton = gtk.SpinButton(adj)
			self.sbutton.show()
			hbox.add(self.sbutton)

			hbbox = gtk.HButtonBox()
			hbbox.set_layout(gtk.BUTTONBOX_END)
			vbox.add(hbbox)
			hbbox.show()

			canbutton = gtk.Button("Cancel", gtk.STOCK_CANCEL)
			canbutton.show()
			hbbox.add(canbutton)
			canbutton.connect_object("clicked", window.destroy, window)

			okbutton = gtk.Button("OK", gtk.STOCK_OK)
			okbutton.show()
			hbbox.add(okbutton)
			okbutton.connect("clicked", self.sigh_okbutton_clicked, window)
		
			self.loadSelection()

			window.show()


	def populateList(self,item):
		"Adds 'item' to ListStore 'list'"
		items = []
		items.append(item)
		self.list.append(items)


	def sigh_okbutton_clicked(self, button, window):
		"Signal Handler for the OK button; saves settings"

		sel = self.tview.get_selection().get_selected()[1]
		if sel:
#			print "Saving %s and %d" % (self.list.get_value(sel,0),  self.sbutton.get_value_as_int())
			self.saveCfg(self.list.get_value(sel,0), self.sbutton.get_value_as_int())
		if self.app is not None:
			self.app.serv = self.app.servlist.getServer(self.list.get_value(sel,0))
			self.app.interval = self.sbutton.get_value_as_int()
			self.app.redraw()
		window.destroy()

	def loadSelection(self):
		"Highlight the saved server in the list"

		iter = self.list.get_iter_first()
		treeselection = self.tview.get_selection()
		Found = False
		while not Found and iter != None:
			if str(self.list.get_value(iter,0)) == str(self.Server):
				path = self.list.get_path(iter)
				treeselection.select_path(path)
				Found = True
			else:
				iter = self.list.iter_next(iter)


	def loadCfg(self):
		"Loads the Configuration"
		self.client = gconf.client_get_default()
		if not self.client.dir_exists('/apps/wowstatus'):
			self.client.add_dir('/apps/wowstatus', gconf.CLIENT_PRELOAD_NONE)
			self.client.set_string('/apps/wowstatus/Server', "Aegwynn")
			self.client.set_int('/apps/wowstatus/Interval', 30)
			self.Server = "Aegwynn"
			self.Interval = 30
		else:

			self.Server = self.client.get_string('/apps/wowstatus/Server')
			self.Interval = self.client.get_int('/apps/wowstatus/Interval')


	def saveCfg(self, srv, int):
		"Saves the configuration"
		self.client = gconf.client_get_default()
		if not self.client.dir_exists('/apps/wowstatus'):
                        self.client.add_dir('/apps/wowstatus', gconf.CLIENT_PRELOAD_NONE)
			srv = self.Server
			int = self.Interval 
		self.client.set_string('/apps/wowstatus/Server', srv)
		self.client.set_int('/apps/wowstatus/Interval', int)


### End of class: Pref



class Server(object):
	"WoW Server object and its properties"
	def __init__(self, name="",type=0,load=0,status=0):
		self.name = name
		self.type = type
		self.load = load
		self.status = status

### End class: Server

class ServerList(object):
	"Parses the XML file and creates a list of Server objects"
	def __init__(self):
		self.servers = []

	def parseLine(self, line):
		i = 0
		if i > len(line):
			return 1
		srv = Server()
		tmp = ""
		if line[i] == '<':
			i += 1
			while i < len(line):
				tmp = ""
				if line[i] == '?':
					return 1
				elif line[i] == 'p':
					return 1

				elif line[i] == 'r' and line[i+1] == 's':
					return 1
				elif line[i] == '/' and line[i-1] == '<':
					return 1
				elif line[i] == 'n':
					i += 3
					while line[i] != "\"":
						tmp += line[i]
						i += 1
					srv.name = tmp.replace('&#039;','\'')
				elif line[i] == 't':
					i += 3	
					srv.type = int(line[i])
					i += 1
				elif line[i] == 's':
					i += 3
					srv.status = int(line[i])
					i += 1
				elif line[i] == 'l':
					i += 3
					srv.load = int(line[i])
					i += 1
				
				i += 1
		
		return srv

	def getInfo(self):
		try:	
			url = urllib.urlopen("http://www.worldofwarcraft.com/realmstatus/status.xml")
			rfile = url.read()
		except IOError, e:
			print "Error occurred when reading the URL: http://www.worldofwarcraft.com/realmstatus/status.xml\n",e.strerror
			return 1
		try:
			file = open("/tmp/wow-status.xml", 'w')
			file.write(rfile)
			file.close()
			f = open('/tmp/wow-status.xml', 'r')
                	line = f.readline()
		except IOError, e:
			print "Error occurred when reading/writing '/tmp/wow-status.xml'\n", e.strerror
			return 1
                i = 0
                srv = Server()
                while line != '':
			srv = self.parseLine(line)
			if srv is not 1:
				self.servers.append(srv)
				del srv
				srv = Server()
			line = f.readline()
		return 0

	def getStatus(self, realm):
		for i in range(0, len(self.servers)):
			if self.servers[i].name == realm:
				return self.servers[i].status

	def getServer(self, realm):
		for i in range(0,len(self.servers)):
			if self.servers[i].name == realm:
				return self.servers[i]

### End class: ServerList


class WSApplet(gnomeapplet.Applet):
	def __init__(self, applet, iid):
	
		pref = Pref(show=False)
		pref.loadCfg()
		self.serv = Server()
		self.__gobject_init__()
		self.applet = applet
		self.updateList()
		self.servlist = ServerList()
		self._srvname = pref.Server
		if self.servlist.getInfo() is 1:
			self.serv.name = "Not Connected"
			self.serv.load = 0
			self.serv.status = 0
		else:
			self.serv = self.servlist.getServer(self._srvname)

		self.box = self.draw()
		
		self.interval = pref.Interval*60*1000
		self.logo = gtk.gdk.pixbuf_new_from_file("/usr/local/bin/wow.png")
		self.timer = gobject.timeout_add(self.interval, self.updateInfo)
		del pref

	def updateInfo(self):
		"Updates the server list and redraws the widgets"
		if self.updateList() is 1:
			self.serv = Server("Not Connected",0,0,0)
			self.redraw()
			return True
		if self.serv.name is "Not Connected":
			self.serv  = Server(self._srvname,0,0,0)

		self.serv = self.servlist.getServer(self.serv.name)
		self.redraw()
		return True

	def updateList(self):
		"Updates the server list and gets each servers' information"
		self.servlist = ServerList()
		return self.servlist.getInfo()

	def draw(self):
		"Initial draw method"
		self.big_evbox = gtk.EventBox()
		self.big_evbox.connect("button-press-event", self.button_press)
		self.hbox = gtk.HBox()
		self.image = gtk.Image()
		if self.serv.status is 1:
			self.image.set_from_file("/usr/local/bin/up.gif")
		else:
			self.image.set_from_file("/usr/local/bin/down.gif")

		self.hbox.add(self.image)

		self.label = gtk.Label(self.serv.name)
		self.tt = gtk.Tooltips()
		if self.serv.load is 0:
			load = "Offline"
		elif self.serv.load is 1:
			load = "Low"
		elif self.serv.load is 2:
			load = "Medium"
		elif self.serv.load is 3:
			load = "High"
		else:
			load = "Max (Queued)"

		self.tt.set_tip(self.label, "Server:  \t" + self.serv.name + "\nLoad:  \t" + load)
		self.hbox.add(self.label)
		self.big_evbox.add(self.hbox)
		self.applet.add(self.big_evbox)
		self.applet.show_all()
		return self.applet

	def redraw(self):
		if self.serv.name is "Not Connected":
			return

		if self.serv.status is 1:
			self.image.set_from_file("/usr/local/bin/up.gif")
		else:
			self.image.set_from_file("/usr/local/bin/down.gif")
		
		self.label.set_text(self.serv.name)

		if self.serv.load is 0:
                        load = "Offline"
                elif self.serv.load is 1:
                        load = "Low"
                elif self.serv.load is 2:
                        load = "Medium"
                elif self.serv.load is 3:
                        load = "High"
                else:
                        load = "Max (Queued)"

		self.tt.set_tip(self.label, "Server:  \t" + self.serv.name + "\nLoad:  \t" + load)

	def button_press(self,widget,event):
		if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
			self.create_menu()
	
	def create_menu(self):
		propxml="""
			<popup name="button3">
			<menuitem name="Item 1" verb="Preferences" label="_Preferences" pixtype="stock" pixname="gtk-properties"/>
			<separator/>
			<menuitem name="Item 3" verb="About" label="_About" pixtype="stock" pixname="gtk-about"/>
			</popup>"""
		verbs = [("Preferences", self.pref_info), ("About", self.about_info)]
		self.applet.setup_menu(propxml, verbs, None)
	
	def pref_info(self, event, data=None):
		prefwin = Pref(applet=self,show=True)
		for i in range(0, len(self.servlist.servers)):
			prefwin.populateList(self.servlist.servers[i].name)
		prefwin.loadSelection()
		self.serv = prefwin.Server
		self._srvname = prefwin.Server
		self.interval = prefwin.Interval*60*1000
		gobject.source_remove(self.timer)
		self.timer = gobject.timeout_add(prefwin.Interval*60*1000, self.updateInfo)
		self.updateInfo()

	def about_info(self,event,data=None):
		about = gnome.ui.About("About WoWStatus Applet","0.2.1","GPLv2",\
                               "GNOME Applet to periodically check the status of a World of Warcraft server",["Matthew Howle <mdhowle@howle.org>"],\
                               ["Matthew Howle <mdhowle@howle.org>"],"Matthew Howle <mdhowle@howle.org>",self.logo)
        	about.show()

	
### End class: WSApplet


def factory(applet, iid):
	WSApplet(applet,iid)
	return True


if len(sys.argv) == 2:
	if sys.argv[1] == "run-in-window":
		main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		main_window.set_title("WoW Server Status")
		main_window.connect("destroy", gtk.main_quit)
		app = gnomeapplet.Applet()
		factory(app, None)
		app.reparent(main_window)
		main_window.show_all()
		gtk.main()
		sys.exit()

gnomeapplet.bonobo_factory("OAFIID:GNOME_WoWStatusApplet_Factory",
				   gnomeapplet.Applet.__gtype__,
				   "World of Warcraft Server Status",
				   "1.0",
				   factory)
